package com.minelx.util.factor;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import static java.util.stream.Collectors.toList;

public class RegisterIterator<E> implements Iterator<List<E>> {

	private final List<Factor<E>> factors;

	private final List<E> cell;

	private final List<Iterator<E>> iterators;

	private boolean firstTrying;

	public RegisterIterator(List<Factor<E>> factors) {
		this.factors = factors;
		// step to end
		iterators = factors.stream()
				.map(Factor::iterator)
				.collect(toList());
		cell = iterators.stream().map(Iterator::next).collect(toList());
		firstTrying = true;
	}

	@Override
	public boolean hasNext() {
		if (firstTrying) {
			return true;
		}
		return iterators.stream().anyMatch(Iterator::hasNext);
	}

	@Override
	public List<E> next() {
		if (firstTrying) {
			firstTrying = false; // never happens
			return packResultSet();
		}
		// move the last iterator forward
		moveForward(iterators.size() - 1);
		return packResultSet();
	}

	private List<E> packResultSet() {
		return new LinkedList<>(cell);
	}

	private void moveForward(int at) {
		Iterator<E> iterator = iterators.get(at);
		if (iterator.hasNext()) {
			E next = iterator.next();
			// bind next to result cell
			cell.set(at, next);
		} else {
			// move upper factor forward
			moveForward(at - 1);
			// clear factor at here
			Iterator<E> newIterator = factors.get(at).iterator();
			E next = newIterator.next();
			cell.set(at, next);
			iterators.set(at, newIterator);
		}
	}
}
